/*
* Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
* Description: none
* Author: s00452708
* Create: 2018-08-01
* Notes: none
* History: 2018-08-01 s00452708 create
 */

#include "cmscbb_config.h"
#if CMSCBB_WITHOUT_SECUREC == 0
#include "securec.h"
#endif
#include "cmscbb_sdk.h"
#include "cmscbb_err_def.h"

#include "stdlib.h"
#include <string.h>
#include <openssl/sha.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#include "openssl/evp.h"
#include <syslog.h>

HASH_ALGO _convert_hash_algo(CVB_UINT32 algo_id)
{
    switch (algo_id) {
        case CMSCBB_HASH_SHA256:
            return HSAG_SHA256;
        case CMSCBB_HASH_SHA384:
            return HSAG_SHA384;
        case CMSCBB_HASH_SHA512:
            return HSAG_SHA512;
        default:
            return HSAG_UNKNOW;
    }
}

static const EVP_MD* _cvb_get_openssl_evpmd(CVB_UINT32 hash_id)
{
    const EVP_MD* md_handler = NULL;
    switch (hash_id) {
        case CMSCBB_HASH_SHA256:
            {
                md_handler = EVP_sha256();
                break;
            }
        case CMSCBB_HASH_SHA384:
            {
                md_handler = EVP_sha384();
                break;
            }
        case CMSCBB_HASH_SHA512:
            {
                md_handler = EVP_sha512();
                break;
            }
        default:
            {
                break;
            }
    }

    return md_handler;
}
CMSCBB_ERROR_CODE CmscbbMalloc(CVB_VOID **ppByte, CVB_SIZE_T size)
{
    if (ppByte == CVB_NULL) {
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    if (size == 0) {
        return CVB_SUCCESS;
    }

    *ppByte = malloc(size);
    if (*ppByte == CVB_NULL) {
        return CMSCBB_ERR_SYS_MEM_ALLOC;
    }

    return CVB_SUCCESS;
}

CVB_VOID CmscbbFree(CVB_VOID *ptr)
{
    if (ptr == CVB_NULL) {
        return;
    }

    free(ptr);
}

CVB_INT CmscbbMemCmp(const CVB_VOID *s1, const CVB_VOID *s2, CVB_SIZE_T n)
{
    if (s1 == NULL || s2 == NULL) {
        return -1;
    }

    return (CVB_INT)memcmp(s1, s2, n);
}

CVB_INT CmscbbStrNCmp(const CVB_CHAR *s1, const CVB_CHAR *s2, CVB_SIZE_T n)
{
    if (s1 == NULL || s2 == NULL) {
        return -1;
    }

    return (CVB_INT)strncmp((const char *)s1, (const char *)s2, n);
}

const CVB_CHAR *CmscbbStrStr(const CVB_CHAR *haystack, const CVB_CHAR *needle)
{
    if (haystack == NULL || needle == NULL) {
        return NULL;
    }

    return (const CVB_CHAR *)strstr((const char *)haystack, (const char *)needle);
}

CVB_CHAR *CmscbbStrChr(const CVB_CHAR *s, CVB_CHAR c)
{
    if (!s) {
        return CVB_NULL;
    }

    while (*s != '\0') {
        if (*s == c) {
            return (CVB_CHAR *)s;
        }
        s++;
    }

    return CVB_NULL;
}

CVB_UINT32 CmscbbStrlen(const CVB_CHAR *s)
{
    if (s == NULL) {
        return 0;
    }

    return (CVB_UINT32)strlen((const char *)s);
}

CVB_INT CmscbbStrCmp(const CVB_CHAR *s1, const CVB_CHAR *s2)
{
    int len1;
    int len2;

    if (s1 == NULL || s2 == NULL) {
        return -1;
    }
    if (s1 == s2) {
        return 0;
    }

    len1 = (int)strlen((const char *)s1);
    len2 = (int)strlen((const char *)s2);
    if (len1 != len2) {
        return -1;
    }

    return strncmp((const char *)s1, (const char *)s2, (size_t)len1);
}

#if CMSCBB_ENABLE_LOG
CVB_VOID CmscbbLogPrint(CMSCBB_LOG_TYPE log_level, const CVB_CHAR *filename, CVB_INT line,
                        const CVB_CHAR *function,
                        CMSCBB_ERROR_CODE rc, const CVB_CHAR *msg)
{
    syslog(log_level, "%s:%d %s: %d, %s", filename, line, function, rc, msg);   
}
#endif

#define DIGEST_SIZE_MD5    16
#define DIGEST_SIZE_SHA1   20
#define DIGEST_SIZE_SHA256 32
#define DIGEST_SIZE_SHA384 48
#define DIGEST_SIZE_SHA512 64

typedef struct crypto_md_st {
#if CMSCBB_STRICT_CHECK
    CVB_UINT32 st_size;
#endif
    const CVB_VOID *message;
    CVB_UINT32 message_len;
    CVB_UINT32 hash_id;
    EVP_MD_CTX * hash_handler;
} CRYPTO_MD;

CMSCBB_ERROR_CODE CmscbbMdCreateCtx(CMSCBB_CRYPTO_MD_CTX *md_ctx)
{
    CMSCBB_ERROR_CODE ret;
    CRYPTO_MD *pMd = CVB_NULL;

    if (md_ctx == CVB_NULL) {
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    ret = CmscbbMalloc((CVB_VOID **)&pMd, sizeof(CRYPTO_MD));
    if (ret != CVB_SUCCESS) {
        return ret;
    }
    ret = memset_s((CVB_VOID *)pMd, sizeof(CRYPTO_MD), 0, sizeof(CRYPTO_MD));
    if (ret != CVB_SUCCESS) {
        return ret;
    }

#if CMSCBB_STRICT_CHECK
    pMd->st_size = sizeof(CRYPTO_MD);
#endif

    *md_ctx = (CMSCBB_CRYPTO_MD_CTX)pMd;
    return CVB_SUCCESS;
}

CMSCBB_ERROR_CODE CmscbbMdInit(CMSCBB_CRYPTO_MD_CTX md_ctx, CVB_UINT32 hash_id)
{
    CMSCBB_ERROR_CODE ret = 0;
    CRYPTO_MD* pMd = (CRYPTO_MD*)md_ctx;
    const EVP_MD* md = NULL;

    if (CVB_NULL == pMd) {
        return -1;
    }

    pMd->hash_handler = EVP_MD_CTX_create();

    EVP_MD_CTX_init(pMd->hash_handler);
    md = _cvb_get_openssl_evpmd(hash_id);
    if (NULL == md) {
        return -1;
    }

    if (!EVP_DigestInit_ex(pMd->hash_handler, md, NULL)) {
        return -1;
    }

    return ret;
}

CMSCBB_ERROR_CODE CmscbbMdUpdate(CMSCBB_CRYPTO_MD_CTX md_ctx, const CVB_BYTE* pbData, CVB_UINT32 len)
{
    CRYPTO_MD *pMd = (CRYPTO_MD*)md_ctx;

    if (CVB_NULL == pMd || CVB_NULL == pMd->hash_handler) {
        return -1;
    }

    if (CVB_NULL == pbData || 0 == len) {
        return 0;
    }

    if (!EVP_DigestUpdate(pMd->hash_handler, (void*)pbData, len)) {
        return -1;
    }

    return CVB_SUCCESS;
}

CMSCBB_ERROR_CODE CmscbbMdFinal(CMSCBB_CRYPTO_MD_CTX md_ctx, CVB_BYTE* pbDigest, CVB_UINT32* len)
{
    int ret;
    CRYPTO_MD *pMd = (CRYPTO_MD*)md_ctx;

    if (CVB_NULL == pMd || CVB_NULL == pMd->hash_handler || CVB_NULL == pbDigest || CVB_NULL == len) {
        return -1;
    }

    ret = EVP_DigestFinal(pMd->hash_handler, pbDigest, (unsigned int*)len);
    if (!ret) {
        return -1;
    }
    return CVB_SUCCESS;
}

CVB_VOID CmscbbMdDestoryCtx(CMSCBB_CRYPTO_MD_CTX md_ctx)
{
    CRYPTO_MD *pMd = (CRYPTO_MD *)md_ctx;

    if (pMd == CVB_NULL) {
        return;
    }

    CmscbbFree(pMd);
}

typedef struct crypto_vrf_st {
#if CMSCBB_STRICT_CHECK
    CVB_UINT32 st_size;
#endif

    CVB_UINT32 st_size;
    void* md_ctx;
    EVP_PKEY* pub;
} CRYPTO_VRF;

CMSCBB_ERROR_CODE CmscbbCryptoVerifyCreateCtx(CMSCBB_CRYPTO_VRF_CTX *ctx)
{
    CMSCBB_ERROR_CODE ret;
    CRYPTO_VRF *pVrf = CVB_NULL;
    CMSCBB_CRYPTO_MD_CTX md_ctx = CVB_NULL;

    if (ctx == CVB_NULL) {
        return CMSCBB_ERR_UNDEFINED;
    }

    ret = CmscbbMalloc((CVB_VOID **)&pVrf, sizeof(CRYPTO_VRF));
    if (ret != CVB_SUCCESS) {
        return ret;
    }
    ret = memset_s((CVB_VOID *)pVrf, sizeof(CRYPTO_VRF), 0, sizeof(CRYPTO_VRF));
    if (ret != CVB_SUCCESS) {
        return ret;
    }

    ret = CmscbbMdCreateCtx(&md_ctx);
    if (ret != CVB_SUCCESS) {
        CmscbbFree(pVrf);
        return CMSCBB_ERR_UNDEFINED;
    }

#if CMSCBB_STRICT_CHECK
    pVrf->st_size = sizeof(CRYPTO_VRF);
#endif
    pVrf->md_ctx = (CRYPTO_MD *)md_ctx;
    *ctx = (CMSCBB_CRYPTO_VRF_CTX)pVrf;
    return ret;
}
#if CMSCBB_SUPPORT_RSAPSS
CMSCBB_ERROR_CODE CmscbbCryptoVerifyInit(CMSCBB_CRYPTO_VRF_CTX vrf_ctx, CmscbbKeyAndAlgInfo *info)
{
    CMSCBB_ERROR_CODE ret;
    CRYPTO_VRF *pVrf = (CRYPTO_VRF *)vrf_ctx;

    if (CVB_NULL == pVrf || CVB_NULL == info || CVB_NULL == info->e || CVB_NULL == info->n) {
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    ret = CmscbbMdInit((CMSCBB_CRYPTO_MD_CTX)pVrf->md_ctx, info->hashAlg);
    if (ret != CVB_SUCCESS) {
        return ret;
    }

    pVrf->n = info->n;
    pVrf->e = info->e;

    if (info->encAlg == CMSCBB_ENC_RSA_PSS) {
        pVrf->alg.fill_alg = ACC_APPEN_EMSA_PSS;
        pVrf->alg.alg_salt = (u16)info->saltLen;
    } else if (info->encAlg == CMSCBB_ENC_RSA) {
        pVrf->alg.fill_alg = ACC_APPEN_EMSA_PKCS_V1_5;
    }

    return CVB_SUCCESS;
}
#else
CMSCBB_ERROR_CODE CmscbbCryptoVerifyInit(CMSCBB_CRYPTO_VRF_CTX vrf_ctx, const CmscbbBigInt *e,
                                         const CmscbbBigInt *n, CVB_UINT32 hash_id)
{
    CMSCBB_ERROR_CODE ret;
    CRYPTO_VRF *pVrf = (CRYPTO_VRF *)vrf_ctx;

    if (pVrf == CVB_NULL || e == CVB_NULL || n == CVB_NULL) {
        return CMSCBB_ERR_CONTEXT_INVALID_PARAM;
    }

    ret = CmscbbMdInit((CMSCBB_CRYPTO_MD_CTX)pVrf->md_ctx, hash_id);
    if (ret != CVB_SUCCESS) {
        return ret;
    }

    return CVB_SUCCESS;
}
#endif
CMSCBB_ERROR_CODE CmscbbCryptoVerifyUpdate(CMSCBB_CRYPTO_VRF_CTX vrf_ctx, const CVB_BYTE *data, CVB_UINT32 len)
{
    CRYPTO_VRF *pVrf = (CRYPTO_VRF *)vrf_ctx;

    if (pVrf == CVB_NULL || pVrf->md_ctx == CVB_NULL) {
        return CMSCBB_ERR_UNDEFINED;
    }

    return CmscbbMdUpdate((CMSCBB_CRYPTO_MD_CTX)pVrf->md_ctx, data, len);
}

CMSCBB_ERROR_CODE CmscbbCryptoVerifyFinal(CMSCBB_CRYPTO_VRF_CTX vrf_ctx, const CVB_BYTE* pbSignature, CVB_UINT32 len, CVB_INT* result)
{
    CMSCBB_ERROR_CODE ret = 0;
    int rc = 0;
    CRYPTO_VRF* pVrf = (CRYPTO_VRF*)vrf_ctx;
    CVB_UINT32 nHashLen;
    if (CVB_NULL == pVrf || CVB_NULL == pVrf->pub || CVB_NULL == pVrf->md_ctx) {
        printf("Para is wrong\r\n");
        return -1;
    }

    nHashLen = ((len + (CVB_UINT32)sizeof(unsigned long) - 1) / (CVB_UINT32)sizeof(unsigned long)) * (CVB_UINT32)sizeof(unsigned long);
    if (len == nHashLen) {
        rc = EVP_VerifyFinal(pVrf->md_ctx, (const unsigned char*)pbSignature, len, pVrf->pub);
    } else {
        CVB_BYTE* pbNew = NULL;

        ret = CmscbbMalloc((CVB_VOID**)&pbNew, nHashLen);
        if (CVB_SUCCESS != ret || NULL == pbNew) {
            return -1;
        }
        memset_s(pbNew, nHashLen, 0, nHashLen);
        ret = (unsigned int)memcpy_s(pbNew + nHashLen - len, len, pbSignature, len);
        if (ret != CVB_SUCCESS) {
            CmscbbFree(pbNew);
            pbNew = NULL;
            return ret;
        }

        rc = EVP_VerifyFinal(pVrf->md_ctx, pbNew, nHashLen, pVrf->pub);
        CmscbbFree(pbNew);
    }

    if (rc == 1) {
        *result = 1;
    } else {
        *result = 0;
    }
    return CVB_SUCCESS;
}



CVB_VOID CmscbbCryptoVerifyDestroyCtx(CMSCBB_CRYPTO_VRF_CTX vrf_ctx)
{
    CRYPTO_VRF *pVrf = (CRYPTO_VRF *)vrf_ctx;

    if (pVrf == CVB_NULL) {
        return;
    }

    CmscbbMdDestoryCtx(pVrf->md_ctx);

    CmscbbFree(pVrf);
}
CVB_FILE_HANDLE CmscbbFileOpen(const CVB_CHAR* path, const CVB_CHAR* mode) {
    return fopen(path, mode);
}

CMSCBB_ERROR_CODE CmscbbFileClose(CVB_FILE_HANDLE fp) {
    return fclose((FILE*)fp);
}

CVB_UINT64 CmscbbFileGetSize(CVB_FILE_HANDLE fp) {
    fseek(fp, 0, SEEK_END);
    return ftell(fp);
}

CVB_SIZE_T CmscbbFileRead(CVB_VOID* ptr, CVB_SIZE_T size, CVB_FILE_HANDLE fp) {
    return fread(ptr, 1, size, fp);
}
